#ifndef PW_PBL_BOX_HPP
#define PW_PBL_BOX_HPP

#include "Allocator.hpp"
#include "Concepts/Pack.hpp"
#include "Math.hpp"
#include "ValueType.hpp"

namespace pw_pbl
{
	/// 类型擦除Box对象，语义上相当于任意对象存储器(类似std::unique_ptr<void>)，自动分配和释放内存，构造和析构由外部手工控制
	/// @note 当ccAllocator<Allocator<APARMS...>>有效时则用Allocator<APARMS...>作为分配器；其次当sizeof...(APARAMS) == 1且ccAllocator<APARMS...[0]>有效时用APARMS...[0]作为分配器
	/// @note Box<vtAllocLayout<LAYOUT>, TAILER>特化使用栈分配
	template <typename... APARMS>
	class Box
	{
		static constexpr bool EIsConfigedAllocator = ccAllocator<Allocator<APARMS...>>;
		using TAllocator = std::conditional_t<EIsConfigedAllocator, Allocator<APARMS...>, ttPackElement<0, APARMS..., void>>;
		static_assert(EIsConfigedAllocator || (sizeof...(APARMS) == 1 && ccAllocator<TAllocator>));

		void *_ptr = nullptr;
		[[no_unique_address]] TAllocator _allocator{};

		inline constexpr Box &self() noexcept { return *this; }
		inline constexpr const Box &self() const noexcept { return *this; }

	protected:
		template <typename T>
		inline void *allocate() noexcept { return self()._ptr = self()._allocator.allocate(sizeof(T), alignof(T)); }
		inline void *allocate(size_t size, size_t align) noexcept { return self()._ptr = self()._allocator.allocate(size, align); }
		inline void deallocate(void) noexcept { self()._allocator.deallocate(self()._ptr), self()._ptr = nullptr; }
		template <typename T>
		inline void *realloc() noexcept { return self()._ptr = self()._allocator.realloc(self()._ptr, sizeof(T), alignof(T)); }
		inline void *realloc(size_t size, size_t align) noexcept { return self()._ptr = self()._allocator.realloc(self()._ptr, size, align); }

	public:
		Box() = default;
		inline constexpr ~Box() noexcept { self()._allocator.deallocate(self()._ptr); }
		inline constexpr Box(Box &&oth) noexcept : _ptr{std::exchange(oth._ptr, nullptr)}, _allocator{std::move(oth._allocator)} {}
		Box(const Box &) = delete;

		template <typename T>
		explicit Box(T &&obj) { OnConstruct<T>(std::forward<T>(obj)); }

		template <std::invocable<void *> F>
		explicit Box(size_t size, size_t align, F &&constructor) { OnConstruct(size, align, std::forward<F>(constructor)); }

		/// 分配、构造，通常用在外部对象的构造函数中
		template <typename T, typename... P>
		inline T *OnConstruct(P &&...p) noexcept
		{
			if (auto ptr = self().template allocate<T>(); ptr)
				return new (ptr) T{std::forward<P>(p)...};
			else
				return nullptr;
		}
		/// 分配、构造，通常用在外部对象的构造函数中
		template <std::invocable<void *> F>
		inline void *OnConstruct(size_t size, size_t align, F &&constructor) noexcept
		{
			if constexpr (std::convertible_to<F, bool>)
				if (!constructor)
					return nullptr;
			if (auto ptr = self().allocate(size, align); ptr)
				return std::forward<F>(constructor)(ptr), ptr;
			else
				return nullptr;
		}
		/// 析构，通常用在外部对象的析构函数中
		template <typename T>
		inline void OnDestruct(void) noexcept { self() ? ((T *)self().get())->~T() : void(); }
		inline void OnDestruct(void (*destructor)(void *)) noexcept { self() ? destructor(self().get()) : void(); }

		/// 析构、释放、分配、构造
		/// @tparam OLD 原有对象类型
		/// @tparam NEW 新建对象类型
		template <typename OLD, typename NEW, typename... P>
		inline NEW *remake(P &&...p) noexcept
		{
			void *ptr;
			if (self())
				((OLD *)self().get())->~OLD(), ptr = self().template realloc<NEW>();
			else
				ptr = self().template allocate<NEW>();
			return ptr ? new (ptr) NEW{std::forward<P>(p)...} : nullptr;
		}

		/// 析构、释放、分配、构造
		/// @param del 原有对象的析构函数
		/// @tparam NEW 新建对象类型
		template <typename NEW, typename... P>
		inline NEW *remake(void (*del)(void *), P &&...p) noexcept
		{
			void *ptr;
			if (self())
				del(self().get()), ptr = self().template realloc<NEW>();
			else
				ptr = self().template allocate<NEW>();
			return ptr ? new (ptr) NEW{std::forward<P>(p)...} : nullptr;
		}

		/// 析构、释放、分配、构造
		/// @tparam OLD 原有对象类型
		/// @tparam NEW 新建对象构造函数
		template <typename OLD, std::invocable<void *> NEW>
		inline void *remake(size_t size, size_t align, NEW &&constructor) noexcept
		{
			if constexpr (std::convertible_to<NEW, bool>)
				if (!constructor)
					return self() ? ((OLD *)self().get())->~OLD() : void(), self().deallcate(), nullptr;
			void *ptr;
			if (self())
				((OLD *)self().get())->~OLD(), ptr = self().template realloc<NEW>();
			else
				ptr = self().allocate(size, align);
			return ptr ? std::forward<NEW>(constructor)(ptr) : nullptr;
		}

		/// 析构、释放、分配、构造
		/// @tparam OLD 原有对象类型
		/// @tparam NEW 新建对象构造函数
		template <std::invocable<void *> NEW>
		inline void *remake(void (*del)(void *), size_t size, size_t align, NEW &&constructor) noexcept
		{
			if constexpr (std::convertible_to<NEW, bool>)
				if (!constructor)
					return self() ? del(self().get()) : void(), self().deallcate(), nullptr;
			void *ptr;
			if (self())
				del(self().get()), ptr = self().template realloc<NEW>();
			else
				ptr = self().allocate(size, align);
			return ptr ? std::forward<NEW>(constructor)(ptr) : nullptr;
		}

	public:
		explicit inline operator bool() const noexcept { return self()._ptr != nullptr; }
		inline void *get() noexcept { return self()._ptr; }
		inline const void *get() const noexcept { return self()._ptr; }

		struct SHandle
		{
			const void *hdl;
			constexpr SHandle(const void *n) noexcept : hdl(n) {}

			auto operator<=>(const SHandle &) const = default;
			inline explicit operator bool() const noexcept { return hdl != nullptr; }
			inline const void *detach() noexcept { return std::exchange(hdl, nullptr); }
		};
		inline constexpr SHandle handle() const noexcept { return {_ptr}; }
	};

	/// 栈分配的Box，除了提供分配接口，还提供尾部存储以方便最大化利用空间（对齐可能留下填充空间）
	/// @tparam LAYOUT 可用于分配的内存布局
	/// @tparam TAILER 尾部类型，通常用于存储虚表指针
	template <AllocLayout LAYOUT, typename TAILER>
	struct Box<vtAllocLayout<LAYOUT>, TAILER>
	{
		static constexpr auto EMaxAlign = std::max(LAYOUT.align, alignof(TAILER));
		static constexpr auto EMaxSize = Math::AlignUp<EMaxAlign>(sizeof(TAILER) + LAYOUT.size) - sizeof(TAILER);

		template <typename T>
		static constexpr bool EAllocable = sizeof(T) <= EMaxSize && alignof(T) <= EMaxAlign;

		template <typename T>
		static constexpr bool allocable_for(const T &) { return EAllocable<T>; }

	private:
		alignas(EMaxAlign) std::byte storage[EMaxSize];
		[[no_unique_address]] TAILER tailer;

	public:
		Box() = default;
		Box(Box &&oth) = delete;
		Box(const Box &) = delete;

		constexpr Box(TAILER tailer) : tailer(std::move(tailer)) {}

		template <typename T> requires(EAllocable<T>)
		constexpr Box(TAILER tailer, T &&obj) : tailer(std::move(tailer)) { new (storage) T(std::forward<T>(obj)); }

		constexpr TAILER &Tailer() { return tailer; }
		constexpr const TAILER &Tailer() const { return tailer; }

		/// 分配、构造，通常用在外部对象的构造函数中
		template <typename T, typename... P> requires(EAllocable<T>)
		inline T *OnConstruct(P &&...p) noexcept { return new (storage) T{std::forward<P>(p)...}; }

		/// 析构，通常用在外部对象的析构函数中
		inline void OnDestruct(void (*destructor)(void *)) noexcept { destructor(storage); }

		void *get() { return storage; }
		const void *get() const { return storage; }

		using SHandle = const Box *;
		constexpr SHandle handle() const { return this; }
	};

	template <AllocLayout LAYOUT, typename... APARMS>
	struct Box<vtAllocLayout<LAYOUT>, APARMS...>
	{
		static_assert(vtAllocLayout<LAYOUT>::ESize != LAYOUT.size, "以vtAllocLayout作为第一个参数的其它形式无效");
	};
} // namespace pw_pbl

#endif // PW_PBL_BOX_HPP
